package com.hmdp.utils;

import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;

/**
 * 全局唯一 id 生成器
 *      -- 为什么要这个 ???
 *          -- 本网站其实是有商品的 就是优惠券 优惠卷分为普通优惠券和秒杀优惠券
 *          -- tb_voucher 存放所有的优惠券 由于秒杀全还有额外信息 所以秒杀券在 tb_seckill_voucher 中会存在
 *          -- 购买秒杀优惠券后会生成订单在 tb_voucher_order 表中
 *          -- 订单的 id 于是就有了需求 必须要全局唯一
 *              -- 首先如果用自增 那如果数据存在多张表中 是可能重复的 而且规律太明显
 *              -- 于是需要手写一个 全局 id 生成器 类似于雪花算法
 *                  -- 算法思路：用一个 long 型整数 他有 64 位 想办法填充这 64 位
 *                      -- 首位 1 表示是正数
 *                      -- 接着 31 位 用来表示时间戳
 *                      -- 剩下来的饿 32 位 用来表示自增的序列号 用 Redis 的 incr 函数实现
 */
@Component
@RequiredArgsConstructor
public class RedisIdWorker {

    // 基准时间戳
    private final long BEGIN_TIMESTAMP = 1640995200L ;
    private final String KEY_PREFIX = "icr:" ;
    private final StringRedisTemplate stringRedisTemplate ;
    private final long COUNT_BITS = 32L ;

    public long nextId(String keyPrefix) {
        // 生成时间戳    当前时间戳减去基准时间戳
        LocalDateTime now = LocalDateTime.now();
        long nowSecond = now.toEpochSecond(ZoneOffset.UTC);
        long timeStamp = nowSecond - BEGIN_TIMESTAMP;   // 最终时间戳
        // 生成序列号    设置一个 key 用它来自增
        // 获取日期 精切到天 一天一个 key
        String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd")) ;
        String key = KEY_PREFIX + keyPrefix + ":" + date ;
        // 获取自增 key
        long count = stringRedisTemplate.opsForValue().increment(key);
        // 拼接并返回
        return timeStamp << COUNT_BITS | count ;
    }

}
